﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace PhysicsApplication
{
    public class Engine
    {
        public List<TrajectoryElements> inegrate()
        {
            elements.Clear();
            double processTime = 0.0;
            double currentRange = 0.0;
            double currentAlt = heightInMeters;
            double velocity = muzzleVelocityMetersPerSec;
            double targetRangeMeters = targetRangeInMeters;
            while ((targetRangeMeters >= currentRange) && (currentAlt >= 0.0))
            {
                TrajectoryElements tempElements = new TrajectoryElements();
                tempElements.range = Converters.metersToFeet(currentRange);
                currentRange = velocity * processTime * Math.Cos(elevationAngle);
                double ProcTimeSquared = Math.Pow(processTime, 2);
                double grav = calculateGravity(currentAlt);
                double sinOfElv = Math.Sin(elevationAngle);
                double equatOne = ((currentAlt * processTime) * sinOfElv);
                double equatTwo = ( (1.0 / 2.0) * (grav * ProcTimeSquared) );
                currentAlt = (equatOne - equatTwo) + currentAlt;
                tempElements.altitude = Converters.metersToFeet(currentAlt);              
                tempElements.elementTime = processTime;
                tempElements.velocity = Converters.metersToFeet(velocity);
                tempElements.kineticEnergy = calculateKineticEnery(velocity);
                elements.Add(tempElements);
                processTime += integrateTime;
                velocity = velocity - calculateDragCoefficient(velocity);
            }
            return elements;
        }
        public bool setMuzzleVelocityAsFeet(String input)
        {
            bool results = false;
            try
            {
                //cast to double and convert to meters
                muzzleVelocityMetersPerSec = Converters.feetToMeters(double.Parse(input));
                results = true;
            }
            catch (Exception ) 
            {
                //TODO: put error message here
                //TODO: add exception if's for System.ArgumentNullException, FormatException & OverflowException
            }           
            return results;
        }
        public bool setElevationAsDegrees(String input)
        {
            bool results = false;
            try
            {
                //cast to double and convert to meters
                elevationAngle = Converters.degreesToRadains(double.Parse(input));
                results = true;
            }
            catch (Exception)
            {
                //TODO: put error message here
                //TODO: add exception if's for System.ArgumentNullException, FormatException & OverflowException
            }  
            return results;
        }
        public bool setHeightAsFeet(String input)
        {
            bool results = false;
            try
            {
                //cast to bool and convert to meters
                heightInMeters = Converters.feetToMeters(double.Parse(input));
                results = true;
            }
            catch (Exception)
            {
                //TODO: put error message here
                //TODO: add exception if's for System.ArgumentNullException, FormatException & OverflowException
            }  
            return results;
        }
        public bool setRangeAsYards(String input)
        {
            bool results = false;
            try
            {
                //cast to bool and convert yard to feet to meters
                targetRangeInMeters = Converters.yardsToMeters(double.Parse(input));
                results = true;
            }
            catch (Exception)
            {
                //TODO: put error message here
                //TODO: add exception if's for System.ArgumentNullException, FormatException & OverflowException
            }
            return results;
        }
        public bool setIntegrateTime(String input)
        {
            bool results = false;
            try
            {
                //cast to bool and convert to meters
                integrateTime = double.Parse(input);
                results = true;
            }
            catch (Exception)
            {
                //TODO: put error message here
                //TODO: add exception if's for System.ArgumentNullException, FormatException & OverflowException
            }
            return results;
        }
        private            
            //double gravity = 9.81;  //current const gravity will calculate later
            double integrateTime = 0.001; //default integrate time setp
            List<TrajectoryElements> elements = new List<TrajectoryElements>(); //collection of trajectory elements
            double muzzleVelocityMetersPerSec = Converters.feetToMeters(1200) ;
            double elevationAngle = 0.1;
            double heightInMeters = Converters.feetToMeters(3) ;
            double targetRangeInMeters = Converters.feetToMeters( 1200 );
            double projectileMass = 115;

            double calculateGravity(double height)
            {
                /*
                 *     g_h = is the gravitational acceleration at height(h), above sea level.
                 *     r_e = is the Earth's mean radius (6.37101x10^6 meters).
                 *     g_0 = is the standard gravitational acceleration (9.80665 m/s^2).
                 * 
                 *     g_h = g_o * ( r_e / (r_e+h) )^2
                 */
                //Universal Gravitational Constant = 6.6726 x 10^-11N-m2/kg2
                double gravitationalAccelerationAtHeight = 0.0;          
                double earthMeanRaduis = 6.37101E6;
                double standardGravitationalAcceleration = 9.80665;
                gravitationalAccelerationAtHeight = standardGravitationalAcceleration * 
                                                  Math.Pow( (earthMeanRaduis / (earthMeanRaduis + height)), 2);
                return gravitationalAccelerationAtHeight;
            }
            double calculateDragCoefficient(double currentSpeed)
            {
                /*
                 *  F_D = is the force of drag,
                 *  d = is the density of the fluid/atmosphere
                 *  v = is the speed of the object relative to the fluid,
                 *  C_d = is the drag coefficient (a dimensionless parameter, e.g. 0.25 to 0.45 for a car)
                 *  A = is the reference area, 
                 *     
                 *  F_D = (1/2) * d * v^2 * C_d * A
                 */
                //currentSpeed = currentSpeed / 3.2808399;
                double atmosphereDensity = 1.22521; // kg/m3 http://wahiduddin.net/calc/density_altitude.htm

                double dragCoefficient = 0.14;
                double area = 9E-3; //nine MM
                double currentForceOfDrag = integrateTime * ((0.5) * (atmosphereDensity * Math.Pow(currentSpeed, 2) * dragCoefficient * area));
                return currentForceOfDrag;
            }
            TrajectoryElements linearInterpolation(TrajectoryElements first, TrajectoryElements second)
            {
                TrajectoryElements impactPoint = new TrajectoryElements();

                return impactPoint;
            }
            double calculateKineticEnery(double velocity)
            {
                return ((1.0/2.0) * projectileMass * velocity * velocity);
            }
    }
}
